#pragma once

#include "comm/my_defs.h"
#include "comm/my_log.h"

typedef enum {
    MY_CONF_TYPE_UNKNOW = 0,
    MY_CONF_TYPE_BOOL   = 1,
    MY_CONF_TYPE_INT64  = 2,
    MY_CONF_TYPE_DOUBLE = 3,
    MY_CONF_TYPE_PTR    = 4
} my_conf_type_t;

typedef union {
    my_bool_t           b;
    int64_t             i;
    double              d;
    void                *p;
} my_conf_val_t;

typedef struct {
    my_conf_type_t      type;
    my_conf_val_t       val;
    my_conf_val_t       def;
    unsigned            has_def:1;
    unsigned            set:1;
} my_conf_item_t;

static inline void
my_conf_item_bool_init(my_conf_item_t *item, my_bool_t has_def, my_bool_t def_val)
{
    item->type = MY_CONF_TYPE_BOOL;
    if (has_def) {
        item->def.b = def_val;
        item->has_def = 1;
    }
}

static inline void
my_conf_item_int64_init(my_conf_item_t *item, my_bool_t has_def, int64_t def_val)
{
    item->type = MY_CONF_TYPE_INT64;
    if (has_def) {
        item->def.i = def_val;
        item->has_def = 1;
    }
}

static inline void
my_conf_item_double_init(my_conf_item_t *item, my_bool_t has_def, double def_val)
{
    item->type = MY_CONF_TYPE_DOUBLE;
    if (has_def) {
        item->def.d = def_val;
        item->has_def = 1;
    }
}

static inline void
my_conf_item_ptr_init(my_conf_item_t *item, my_bool_t has_def, void *def_val)
{
    item->type = MY_CONF_TYPE_PTR;
    if (has_def) {
        item->def.p = def_val;
        item->has_def = 1;
    }
}

static inline void
my_conf_item_bool_set(my_conf_item_t *item, my_bool_t val)
{
    if (item->type != MY_CONF_TYPE_BOOL) {
        MY_LOG_FATAL("conf item type no match");
    }

    item->val.b = val;
    item->set = 1;
}

static inline void
my_conf_item_int64_set(my_conf_item_t *item, int64_t val)
{
    if (item->type != MY_CONF_TYPE_INT64) {
        MY_LOG_FATAL("conf item type no match");
    }

    item->val.i = val;
    item->set = 1;
}

static inline void
my_conf_item_double_set(my_conf_item_t *item, double val)
{
    if (item->type != MY_CONF_TYPE_DOUBLE) {
        MY_LOG_FATAL("conf item type no match");
    }

    item->val.d = val;
    item->set = 1;
}

static inline void
my_conf_item_ptr_set(my_conf_item_t *item, void *val)
{
    if (item->type != MY_CONF_TYPE_PTR) {
        MY_LOG_FATAL("conf item type no match");
    }

    item->val.p = val;
    item->set = 1;
}

static inline my_bool_t
my_conf_item_bool_get(my_conf_item_t *item)
{
    if (item->type != MY_CONF_TYPE_BOOL) {
        MY_LOG_FATAL("conf item type no match");
    }

    if (item->set) {
        return item->val.b;
    }

    if (item->has_def) {
        return item->def.b;
    }

    MY_LOG_FATAL("conf item no set");
    return MY_FALSE;
}

static inline int64_t
my_conf_item_int64_get(my_conf_item_t *item)
{
    if (item->type != MY_CONF_TYPE_INT64) {
        MY_LOG_FATAL("conf item type no match");
    }

    if (item->set) {
        return item->val.i;
    }

    if (item->has_def) {
        return item->def.i;
    }

    MY_LOG_FATAL("conf item no set");
    return 0;
}

static inline double
my_conf_item_double_get(my_conf_item_t *item)
{
    if (item->type != MY_CONF_TYPE_DOUBLE) {
        MY_LOG_FATAL("conf item type no match");
    }

    if (item->set) {
        return item->val.d;
    }

    if (item->has_def) {
        return item->def.d;
    }

    MY_LOG_FATAL("conf item no set");
    return 0;
}

static inline void *
my_conf_item_ptr_get(my_conf_item_t *item)
{
    if (item->type != MY_CONF_TYPE_PTR) {
        MY_LOG_FATAL("conf item type no match");
    }

    if (item->set) {
        return item->val.p;
    }

    if (item->has_def) {
        return item->def.p;
    }

    MY_LOG_FATAL("conf item no set");
    return NULL;
}